// 动态规划 - 核心 5 步：
// 1. 确定状态表示 - 根据 题目要求，经验，发现重复子问题 确定状态表示
// 2. 推导状态转移方程: dp[i] = ?
//    用 之前的状态 或者 之后的状态 推导当前的状态（根据最近一步划分问题）
// 3. 初始化：保证填表时不越界
// 4. 确定填表顺序：填写当前状态值的时候，所需状态的值已经计算过了
// 5. 返回值：结合题目要求 + 状态表示

// 经典题目：斐波那契数列模型

// 技巧：
// dp[] 表多开一个长度，处理数组越界及初始化复杂的问题
// 结合滚动数组优化 - 注意赋值顺序

// 例题 4：
// 一条包含字母 A-Z 的消息通过以下映射进行了 编码 ：
//
//        "1" -> 'A'
//
//        "2" -> 'B'
//
//        ...
//
//        "25" -> 'Y'
//
//        "26" -> 'Z'
//
//        然而，在 解码 已编码的消息时，你意识到有许多不同的方式来解码，因为有些编码被包含在其它编码当中（"2" 和 "5" 与 "25"）。
//
//        例如，"11106" 可以映射为：
//
//        "AAJF" ，将消息分组为 (1, 1, 10, 6)
//        "KJF" ，将消息分组为 (11, 10, 6)
//        消息不能分组为  (1, 11, 06) ，因为 "06" 不是一个合法编码（只有 "6" 是合法的）。
//        注意，可能存在无法解码的字符串。
//
//        给你一个只含数字的 非空 字符串 s ，请计算并返回 解码 方法的 总数 。如果没有合法的方式解码整个字符串，返回 0。
//
//        题目数据保证答案肯定是一个 32 位 的整数。
//
//
//
//        示例 1：
//
//        输入：s = "12"
//        输出：2
//        解释：它可以解码为 "AB"（1 2）或者 "L"（12）。
//        示例 2：
//
//        输入：s = "226"
//        输出：3
//        解释：它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
//        示例 3：
//
//        输入：s = "06"
//        输出：0
//        解释："06" 无法映射到 "F" ，因为存在前导零（"6" 和 "06" 并不等价）。
//
//
//        提示：
//
//        1 <= s.length <= 100
//        s 只包含数字，并且可能包含前导零。


// 解题思路：
// dp[i] 以 i 位置为结尾的解码方法总数
// s[i] 单独解码成功: dp[i - 1], 失败：0
// s[i] 结合 s[i - 1] 解码成功: dp[i - 2], 失败: 0
// dp[i] = dp[i - 1] + dp[i - 2]
// 1 <= s[0] <= 9: dp[0] = 1, s[0] == 0: dp[0] = 0
// 10 <= s[0]s[1] <= 26, s[1] != 0: dp[1] = 2
// s[0]s[1] < 10 || s[0]s[1] > 26 , s[1] != 0: dp[1] = 1
// s[0]s[1] < 10 || s[0]s[1] > 26 , s[1] == 0: dp[1] = 0
// 从左往右填表
// 返回 dp[n - 1]

// 使用技巧优化：
// dp[] = int[n + 1];
// s[0]单独解码能够成功: dp[1] = 1;
// s[0] 和 s[1] 结合解码成功: dp[0] = 1;
// dp[2] = dp[1] + dp[2]
// dp[i] = dp[i - 1 + dp[i - 2]
// 从左往右填表
// 返回 dp[n]

public class NumDecodings {
    public int numDecodings(String s) {
        int n = s.length();
        char[] sArr = s.toCharArray();
        int[] dp = new int[n + 1];

        dp[0] = 1;
        if(sArr[0] != '0') dp[1] = 1;
        else dp[1] = 0;

        for(int i = 2; i <= n; i++){
            if(sArr[i - 1] != '0') dp[i] += dp[i - 1];
            int tmp = (sArr[i - 2] - '0') * 10 + sArr[i - 1] - '0';
            if(tmp >= 10 && tmp <= 26) dp[i] += dp[i - 2];
        }

        return dp[n];
    }
}
